﻿using System;
using System.Collections.Generic;
using System.Text;
using SsLib;
using System.Reflection;

namespace SscLib
{
    public class SymbolTable
    {        

        //The absolute top scope of the symbol table;
        ScopeSymbol topScope;

        public static TypeSymbol Integer;
        public static TypeSymbol Short;
        public static TypeSymbol Character;
        public static TypeSymbol Text;
        public static TypeSymbol Real;
        public static TypeSymbol Long;
        public static TypeSymbol String;
        public static FieldSymbol NoText;
        public static TypeSymbol NoType;
        public static TypeSymbol RefText;
        public static TypeSymbol Boolean;

        public static TypeSymbol SimProg;
        public static TypeSymbol SimObject;

        public static TypeSymbol Array;

        private ScopeSymbol builtinTypes;
        private ScopeSymbol systemClasses;
        private ScopeSymbol hiddenTypes;

        private ScopeSymbol objectRoot;

        public ScopeSymbol TopScope
        {
            get
            {
                return this.topScope;
            }
        }

        public List<TypeSymbol> ArrayTypes = new List<TypeSymbol>();

        public SymbolTable()
        {
            this.builtinTypes = new ModuleSymbol();

            builtinTypes.AddType("Object", null, typeof(object));

            String = builtinTypes.AddType("String", null, typeof(string));

            NoType = builtinTypes.AddType("NoType", null, typeof(void));

            Boolean = builtinTypes.AddType("Boolean", null, typeof(bool));
            Character = builtinTypes.AddType("Character", null, typeof(char));

            Integer = builtinTypes.AddType("Integer", null, typeof(int));
            Long = builtinTypes.AddType("Long", null, typeof(double));
            Short = builtinTypes.AddType("Short", null, typeof(short));
            Real = builtinTypes.AddType("Real", null, typeof(float));

            ImportType(builtinTypes, typeof(SsLib.Text), "Text");
            Text = builtinTypes.ResolveType("Text");
            NoText = Text.ResolveField("NoText");

            this.systemClasses = new ModuleSymbol(this.builtinTypes);

            //ImportType(systemClasses, typeof(SsLib.SimObject), "SimObject");
            ImportType(systemClasses, typeof(SsLib.BasicIO.InFile), "InFile");
            ImportType(systemClasses, typeof(SsLib.BasicIO.OutFile), "OutFile");
            ImportType(systemClasses, typeof(SsLib.BasicIO.PrintFile), "PrintFile");
            ImportType(systemClasses, typeof(SsLib.BasicIO.DirectFile), "DirectFile");
            ImportType(systemClasses, typeof(SsLib.BasicIO.InByteFile), "InByteFile");
            ImportType(systemClasses, typeof(SsLib.BasicIO.OutByteFile), "OutByteFile");
            ImportType(systemClasses, typeof(SsLib.BasicIO.DirectByteFile), "DirectByteFile");
            ImportType(systemClasses, typeof(SsLib.SimSet), "SimSet");
            ImportType(systemClasses, typeof(SsLib.Simulation), "Simulation");

            this.hiddenTypes = new ModuleSymbol(systemClasses);

            this.hiddenTypes.AddType("Array", null, typeof(System.Array));

            //ImportType(hiddenTypes, typeof(SsLib.SimObject), "SimObject");
            ImportType(builtinTypes, typeof(SsLib.SimObject), "SimObject");

            ImportType(hiddenTypes, typeof(SsLib.BasicIO), "BasicIO");

            Array = this.hiddenTypes.ResolveType("Array");

            SimObject = this.hiddenTypes.ResolveType("SimObject");

            FieldSymbol f = null;

            f = SimObject.AddField("_basicio", hiddenTypes.ResolveType("BasicIO"));
            f.Info = typeof(SsLib.SimObject).GetField("_basicio", BindingFlags.Instance | BindingFlags.NonPublic);
            SimObject.InspectField(f);

            f = SimObject.AddField("_sysout", systemClasses.ResolveType("PrintFile"));
            f.Info = typeof(SsLib.SimObject).GetField("_sysout", BindingFlags.Instance | BindingFlags.NonPublic);
            SimObject.InspectField(f);

            f = SimObject.AddField("_sysin", systemClasses.ResolveType("InFile"));
            f.Info = typeof(SsLib.SimObject).GetField("_sysin", BindingFlags.Instance | BindingFlags.NonPublic);
            SimObject.InspectField(f);


            ImportType(hiddenTypes, typeof(SsLib.SimProg), "SimProg");

            SimProg = hiddenTypes.ResolveType("SimProg");

            FieldSymbol _sysout = SimProg.AddField("_sysout", systemClasses.ResolveType("PrintFile"));
            _sysout.Info = typeof(SsLib.SimProg).GetField("_sysout", BindingFlags.Instance | BindingFlags.NonPublic);

            FieldSymbol _sysin = SimProg.AddField("_sysin", systemClasses.ResolveType("InFile"));
            _sysin.Info = typeof(SsLib.SimProg).GetField("_sysin", BindingFlags.Instance | BindingFlags.NonPublic);

            //FieldSymbol _basicio = SimObject.AddField("_basicio", hiddenTypes.ResolveType("BasicIO"));
            //_basicio.Info = typeof(SsLib.SimObject).GetField("_basicio", BindingFlags.Instance | BindingFlags.NonPublic);

            SimProg.InspectField(_sysout);
            SimProg.InspectField(_sysin);



            objectRoot = SimObject;

            //objectRoot = objectRoot.InspectField(objectRoot.ResolveField("_basicio"));
            //objectRoot = objectRoot.InspectField(objectRoot.ResolveField("_sysin"));
            //objectRoot = objectRoot.InspectField(objectRoot.ResolveField("_sysout"));

          
            //FieldSymbol _sysin = SimProg.AddField("_sysin", topScope.ResolveType("InFile"));
            //_sysin.Info = typeof(SsLib.SimProg).GetField("_sysin", BindingFlags.Instance | BindingFlags.NonPublic);

            //FieldSymbol _sysout = SimProg.AddField("_sysout", topScope.ResolveType("PrintFile"));
            //_sysout.Info = typeof(SsLib.SimProg).GetField("_sysout", BindingFlags.Instance | BindingFlags.NonPublic);

            topScope = new ModuleSymbol(systemClasses);
        }


        public void ImportType(ScopeSymbol scope, Type t, string name)
        {
            TypeSymbol baseType = scope.ResolveType(t.BaseType.Name);
            TypeSymbol tSym = scope.AddType(name, baseType, t);

            if (!t.IsValueType)
            {
                ConstructorInfo[] ctors = t.GetConstructors();
                //if (ctors.Length == 0) throw new Exception("Type '" + name + "' has 0 ctors.");
                //if (ctors.Length > 1) throw new Exception("Type '" + name + "' has more then 1 ctors.");
                //TODO: handle more consructors (BasicIO has more than one)
                if (ctors.Length > 0)
                {
                    ConstructorInfo ctor = ctors[0];

                    //Todo: handle parameters
                    CtorSymbol cSym = tSym.AddCtor();
                    cSym.Info = ctor;

                    foreach (ParameterInfo pi in ctor.GetParameters())
                    {
                        cSym.AddArgument(pi.Name, scope.ResolveType(pi.ParameterType.Name));
                    }
                }
            }

            ImportMethods(tSym, t);
        }

        public void ImportMethods(ScopeSymbol scope, Type t)
        {
            MethodInfo[] meths = t.GetMethods(/*BindingFlags.DeclaredOnly | */
                BindingFlags.Instance | BindingFlags.Public);
            
            foreach (MethodInfo mi in meths)
            {
                if (mi.DeclaringType == typeof(Object) || (false))
                  //  mi.DeclaringType == typeof(SsLib.SimObject) ||
                    //mi.Name == "Prolog" ) //||
                   //  mi.Name.StartsWith("_") ) 
                    continue;

                MethodSymbol mSym = scope.AddMethod(mi.Name, scope.ResolveType(mi.ReturnType.Name));
                mSym.Info = mi;

                foreach (ParameterInfo pi in mi.GetParameters())
                {
                    mSym.AddArgument(pi.Name, scope.ResolveType(pi.ParameterType.Name));
                }
            }

            foreach (FieldInfo fi in t.GetFields())
            {
                //if (fi.Name.StartsWith("_")) continue;

                FieldSymbol fSym = scope.AddField(fi.Name, scope.ResolveType(fi.FieldType.Name));
                fSym.Info = fi;
            }

        }


        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            ScopeSymbol scope = topScope;

            while (scope != null)
            {
                sb.AppendLine("SymbolTable Scope:");
                sb.AppendLine(scope.ToString());
                scope = scope.Scope;
            }
            
            return sb.ToString();
        }
    }
}
